///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef __BUOLA_TTY_CLINEREADER_H__
#define __BUOLA_TTY_CLINEREADER_H__

#include <buola/buola.h>
#include <buola/tty/ccompletion.h>
#include <buola/tty/ccontrolledio.h>
#include <buola/utility/ccursorpos.h>
#include <buola/utility/usignal.h>
#include <buola/io/curi.h>
#include <set>

namespace buola { namespace tty {

///\addtogroup tty
///@{

class CLineReader : public UUncopiable
{
    enum EChange
    {
        CHANGE_NONE=0,
        CHANGE_CURSOR=(1<<0),
        CHANGE_SELECTION=(1<<1),
        CHANGE_TEXT=(1<<2),
        CHANGE_NUMARG=(1<<3),
        CHANGE_PROMPT=(1<<4),
        CHANGE_SEARCHPOS=(1<<5),
        CHANGE_MESSAGE=(1<<6),
        CHANGE_COMPLETE=(1<<7),
        CHANGE_INSERTCHAR=(1<<8)
    };
    
    DECLARE_FRIEND_ENUM_BITOPS(EChange);

    enum EMode
    {
        MODE_NORMAL=0,
        MODE_SEARCH=(1<<0),
        MODE_USUAL=(1<<1),
        MODE_COMPLETE=(1<<2),
        MODE_NUMARG=(1<<3),
        MODE_MESSAGE=(1<<4),
        MODE_REGEX=(1<<5),
        
        MODE_ANYSEARCH=MODE_SEARCH|MODE_USUAL|MODE_REGEX
    };
    
    DECLARE_FRIEND_ENUM_BITOPS(EMode);

public:
    CLineReader(CScreen *pScreen=nullptr);
    ~CLineReader();
    
private:
    void CalcFormat();
    CCursorPos GetCursorPos(int pCursor);
    void Redraw(int pFrom=-1);

    const std::wstring &GetSearchResult(int i);
    
    void PrintCompletion();
    void PrintAnySearch(int pLines);

    void RecalcPrompt();
    void RecalcText();
    void RecalcCompletion();
    void RecalcUsual();
    void RecalcSearch(int pNum);
    void RecalcRegex(int pNum);
    void RecalcAnySearch(int pNum);
    void RecalcAndRedraw();

    void UpdateCompletion();
    
    void ChangedNone();
    void Changed(EChange pChange);
    void Changed(EChange pChange,int pFrom);
    
    void CommandHistoryBack();
    void CommandHistoryForward();
    void CommandHistoryFirst();
    void CommandHistoryLast();
    void CommandMoveCursor(int pPos,bool pSelect);
    void CommandCharLeft(bool pSelect);
    void CommandCharRight(bool pSelect);
    void CommandWordLeft(bool pSelect);
    void CommandWordRight(bool pSelect);
    void CommandHome(bool pSelect);
    void CommandEnd(bool pSelect);
    void CommandDeselect();
    void CommandMark();
    void CommandDeleteSelection();
    void CommandDelete();
    void CommandBackspace();
    void CommandRemoveToBOL();
    void CommandRemoveToEOL();
    void CommandEnter();
    void CommandCancel();
    void CommandEOF();
    void CommandComplete();
    void CommandCompleteMove(EKeyCode pKey);
    void CommandCompleteAccept();
    void CommandExpand();
    void CommandCutLine();
    void CommandPasteLine();
    void CommandSearch();
    void CommandRegex();
    void CommandSearchMove(int pMove);
    void CommandSearchAccept(bool pExec,bool pCursorToStart);
    void CommandInsertChar(wchar_t pCh);
    void CommandNumArg(int pNum);
    void CommandUsual();

    void ProcessChar(wchar_t pCh);
    void ProcessCharNumArg(wchar_t pCh);
    void ProcessCharComplete(wchar_t pCh);
    void ProcessCharSearch(wchar_t pCh);
    void ProcessCharNormal(wchar_t pCh);
    
public:
    void SetPrompt(const std::wstring &pText);
    std::wstring ReadWLine(const std::wstring &pStart=std::wstring());
    std::string ReadLine()                           {   return utf8(ReadWLine()); }
    std::string ReadLine(const std::string &pStart)      {   return utf8(ReadWLine(utf32(pStart))); }

    bool IsEOF()            {   return mEOF;    }
    
    void LoadHistory(const io::CURI &pURL);
    void SaveHistory(const io::CURI &pURL);
    void AddToHistory(const std::wstring &pText);

    void SetIndentPositions(const std::vector<int> &pPos)   {   mIndentPositions=pPos;  }
public:
    USignal<bool(const std::string&,int,CCompletion<std::string>&)> sComplete;
    USignal<bool(const std::wstring&,int,CCompletion<std::wstring>&)> sCompleteW; //wide-char version
    USignal<bool(std::wstring&,int&)> sExpandW;

private:
    EMode mMode;
    
    CControlledIO mIO;
    
    std::wstring mText;
    std::vector<CControlledIO::SBlock> mTextBlocks;
    int mCursor; //as offset into mText    
    CCursorPos mTextEndPos;
    
    bool mEOF;

    struct
    {
        std::wstring mSet;
        std::wstring mEffective;
        CCursorPos mOP;
        std::vector<CControlledIO::SBlock> mBlocks;
    } mPrompt;
    
    struct
    {
        int mFirstNew;
        std::vector<std::wstring> mLines;
        std::wstring mCurrent;
        int mPos;
    } mHistory;

    struct SGroupInfo
    {
        int mCols;          //number of columns for this group
        int mWidth;         //width of each column in this group
        int mFirstIndex;    //as an index in all the completion options
    };
    
    struct
    {
        CCompletion<std::wstring> mResult;

        int mCount;
        std::wstring mPrefix;
        std::vector<SGroupInfo> mGroups;
        std::vector<int> mPages;    //starting index of each page
        int mPage;
        std::wstring mLastText;
        int mCursor;
    } mCompletion;
    
    struct
    {
        EChange mType;
        int mFrom;
    } mChanged;

    struct
    {
        int mB,mE;
    } mSelect;

    struct
    {
        std::vector<int> mResults;
        std::set<std::wstring> mAlready;
        int mSearched; //last from history already searched
    } mSearch;

    struct
    {
        std::map<std::wstring,int> mMap;
        int mLastUpdate;
        std::vector<std::map<std::wstring,int>::iterator> mMatches;
        std::wstring mLastText;
    } mUsual;

    struct 
    {
        int mFirst; //first currently displayed
        int mPos;   //position of search cursor
        int mCount;
    } mAnySearch;
    
    int mNumArg;
    std::wstring mMessage;
    
    std::wstring mCutBuffer;
    bool mFinishLine;

    int mResultLines;
    
    std::vector<int> mIndentPositions;
};

///@}

/*namespace tty*/ } /*namespace buola*/ }

#endif
